/*
 * ShowOutlineAction.java
 * 
 * Copyright (c) 2007
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.dcarew.outlinemenu;

import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IParent;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.ISourceReference;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.ui.JavaPlugin;
import org.eclipse.jdt.internal.ui.javaeditor.CompilationUnitEditor;
import org.eclipse.jdt.ui.IWorkingCopyManager;
import org.eclipse.jdt.ui.JavaElementLabelProvider;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.jface.viewers.DecoratingLabelProvider;
import org.eclipse.jface.viewers.ILabelDecorator;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.IWorkbenchWindowPulldownDelegate;

// TODO: show the tree structure better - don't just use spaces

// TODO: use submenus for inner classes?

// TODO: show compilation errors -

// TODO: in the (far) future, have this pulldown menu work for everything that contributes to the outline view


/**
 * Adds a class / method pulldown menu to the workspace toolbar.
 * 
 * @author Devon Carew
 */
public class ShowOutlineAction
	extends Action
	implements IWorkbenchWindowPulldownDelegate
{
	private IWorkbenchWindow 	workbenchWindow;
	private ILabelProvider		labelProvider;
	
	private Menu 				oldMenu;
	
	public ShowOutlineAction()
	{
		
	}
	
	public Menu getMenu(Control parent)
	{
		if (oldMenu != null)
			oldMenu.dispose();
		
		oldMenu = createMenu(parent);
		
		return oldMenu;
	}

	public void init(IWorkbenchWindow window)
	{
		this.workbenchWindow = window;
		
		JavaElementLabelProvider javaLabelProvider = new JavaElementLabelProvider(
			JavaElementLabelProvider.SHOW_PARAMETERS | JavaElementLabelProvider.SHOW_OVERLAY_ICONS);
		
		ILabelDecorator javaLabelDecorator = workbenchWindow.getWorkbench().getDecoratorManager().getLabelDecorator();
		
		labelProvider = new DecoratingLabelProvider(javaLabelProvider, javaLabelDecorator);
	}

	public void dispose()
	{
		if (oldMenu != null)
			oldMenu.dispose();
		
		labelProvider.dispose();
	}
	
	public void run(IAction action)
	{
		// Do we really want to select an element on a button press? the button is right next to the
		// pulldown arrow, and the user could hit it by accident. This would make them loose their
		// current place in the file.

//		// On a button press, select the main type in the file.
//		CompilationUnitEditor javaEditor = getCompilationUnitEditor();
//		
//		if (javaEditor == null)
//			return;
//		
//		ICompilationUnit compilationUnit = getCompilationUnit(javaEditor);
//		
//		javaEditor.setSelection(compilationUnit.findPrimaryType());
	}

	public void selectionChanged(IAction action, ISelection selection)
	{
		
	}
	
	private CompilationUnitEditor getCompilationUnitEditor()
	{
		IEditorPart editorPart = null;
		
		if (workbenchWindow != null && workbenchWindow.getActivePage() != null)
			editorPart = workbenchWindow.getActivePage().getActiveEditor();
		
		if (editorPart instanceof CompilationUnitEditor)
			return (CompilationUnitEditor)editorPart;
		
		return null;
	}
	
	private Menu createMenu(Control parent)
	{
		CompilationUnitEditor javaEditor = getCompilationUnitEditor();
		
		if (javaEditor == null)
			return null;
		
		ICompilationUnit compilationUnit = getCompilationUnit(javaEditor);
		
		Menu menu = new Menu(parent);
		
		ISelection selection = javaEditor.getSelectionProvider().getSelection();
		
		try
		{
			traverse(menu, compilationUnit, selection);
		}
		catch (JavaModelException exception)
		{
			OutlinePlugin.log(exception);
		}
		
		return menu;
	}
	
	private ICompilationUnit getCompilationUnit(CompilationUnitEditor javaEditor)
	{
		IWorkingCopyManager manager = JavaPlugin.getDefault().getWorkingCopyManager();
		
		return manager.getWorkingCopy(javaEditor.getEditorInput());
	}
	
	private void selectElement(IJavaElement element)
	{
		CompilationUnitEditor javaEditor = getCompilationUnitEditor();
		
		if (javaEditor != null)
			javaEditor.setSelection(element);
	}
	
	private MenuItem selectedMenuItem;
	
	private void traverse(Menu menu, ICompilationUnit compilationUnit, ISelection selection)
		throws JavaModelException
	{
		selectedMenuItem = null;
		
		IJavaElement[] children = compilationUnit.getChildren();
		
		for (int i = 0; i < children.length; i++)
		{
			traverse(menu, children[i], "", 0, selection);
		}
	}
	
	private void traverse(Menu menu, IJavaElement element, String offset, int offsetCount, ISelection selection)
		throws JavaModelException
	{
		if (element instanceof IType || element instanceof IMethod)
		{
			MenuItem menuItem = new MenuItem(menu, SWT.CHECK);
			
			//menuItem.setText(offset + labelProvider.getText(element));
			menuItem.setText(labelProvider.getText(element));
			menuItem.setImage(labelProvider.getImage(element));
			menuItem.setData(element);
			
			// If the selection is completely contained in a method, then select that method.
			// If we run across another method for which the same is true, select that method 
			// and de-select the old one.
			if (element instanceof ISourceReference)
			{
				if (selectionContainedInElement((ISourceReference)element, selection))
				{
					if (selectedMenuItem != null)
						selectedMenuItem.setSelection(false);
					
					menuItem.setSelection(true);
					
					selectedMenuItem = menuItem;
				}
			}
			
			menuItem.addSelectionListener(new SelectionListener() {
				public void widgetDefaultSelected(SelectionEvent event) {
					
				}
				public void widgetSelected(SelectionEvent event) {
					selectElement((IJavaElement)event.widget.getData());
				}
			});
			
			if (element instanceof IParent)
			{
				IJavaElement[] children = ((IParent)element).getChildren();
				
				for (int i = 0; i < children.length; i++)
				{
					traverse(menu, children[i], offset + "   ", offsetCount + 1, selection);
				}
			}
		}
	}
	
	private boolean selectionContainedInElement(ISourceReference element, ISelection isel)
	{
		if (isel instanceof TextSelection)
		{
			try
			{
				TextSelection 	selection = (TextSelection)isel;
				ISourceRange 	sourceRange = element.getSourceRange();
				
				if (sourceRange == null)
					return false;
				
				if ((sourceRange.getOffset() <= selection.getOffset()) &&
					(sourceRange.getOffset() + sourceRange.getLength() >= selection.getOffset() + selection.getLength()))
				{
					return true;
				}
			}
			catch (JavaModelException e)
			{
				return false;
			}
		}
		
		return false;
	}
	
}
